package com.ruoyi.worker.service.workerSys.impl;

import cn.afterturn.easypoi.excel.ExcelImportUtil;
import cn.afterturn.easypoi.excel.entity.ImportParams;
import cn.hutool.core.util.StrUtil;
import com.google.common.base.Joiner;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Sets;
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.exception.CustomException;
import com.ruoyi.common.utils.DateUtils;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.worker.constant.Constant;
import com.ruoyi.worker.domain.MonthlyAttendanceIni;
import com.ruoyi.worker.domain.TermAttendanceRules;
import com.ruoyi.worker.domain.workAttendance.*;
import com.ruoyi.worker.domain.workAttendance.vo.AttendanceMonthRecordDto;
import com.ruoyi.worker.domain.workAttendance.vo.AttendanceMonthRecordVo;
import com.ruoyi.worker.mapper.attendance.AttendanceDailyRecordMapper;
import com.ruoyi.worker.mapper.attendance.RzScheduleStaffPlanMapper;
import com.ruoyi.worker.service.workerSys.WorkAttendanceService;
import org.apache.commons.compress.utils.Lists;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.Resource;
import java.io.InputStream;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @ClassName WorkAttendanceServiceimpl
 * @Description 考勤管理service
 * @Author leizhang
 * Date 2021/11/16 2:18 下午
 * @Version 1.0
 **/
@Service
public class WorkAttendanceServiceImpl implements WorkAttendanceService {
    private Logger LOGGER = LoggerFactory.getLogger(WorkAttendanceServiceImpl.class);
    @Resource
    private AttendanceDailyRecordMapper workAttendanceMapper;

    @Resource
    private RzScheduleStaffPlanMapper rzScheduleStaffPlanMapper;

    /**
     * @param requestParam {"workTime":"考勤时间"}
     * @return null
     * @Description : 1.根据时间查询出对排班对象 TODO 此接口应为定时器触发
     * 2.排班规则,
     * 3.出勤数据，清理
     * 4.统计当日考勤结果
     * @Author : leizhang
     * @Date 2:19 下午 2021/11/16
     **/
    @Transactional(rollbackFor = Exception.class)
    @Override
    public void saveWorkAttendanceInfo(Map<String, Object> requestParam) throws Exception {
        if (null == requestParam.get(Constant.WORK_TIME) || StrUtil.isEmpty(requestParam.get(Constant.WORK_TIME).toString())) {
            throw new CustomException("缺少必传参数work_time");
        }
        String workTime = requestParam.get(Constant.WORK_TIME).toString();
        //1.获取通勤数据
        requestParam.put(Constant.AFTER_WORK_TIME, calculateDate(workTime, 1));
        List<MonthlyAttendanceIni> iniList = workAttendanceMapper.listOfAttendancePassRecord(requestParam);
        //---------------fixed 2021-12-02 考勤机记录修改----------------
        Map<String, List<MonthlyAttendanceIni>> groupByIdCard = iniList.stream().collect(Collectors.groupingBy(MonthlyAttendanceIni::getIdCard));
        if (iniList.isEmpty()) {
            throw new CustomException(String.format("%s没有对应的考勤数据", workTime));
        }
        //2.查询通用考勤规则
        TermAttendanceRules termAttendanceRules = workAttendanceMapper.listOfTermAttendanceRules(requestParam);
        if (null == termAttendanceRules) {
            throw new CustomException("通用考勤规则为空");
        }
        //3.根据时间查询已经排班对象
        List<AttendanceDailyRecord> attendanceDailyRecords = workAttendanceMapper.listOfWorkAttendanceInformation(requestParam);
        if (attendanceDailyRecords.isEmpty()) {
            throw new CustomException(String.format("%s无排班记录", workTime));
        }
        //4.----------------------计算考勤结果 fixed 2021-12-02 考勤机数据变动无需考勤通行记录分组--------------
//        calculateAttendanceResult(requestParam, attendanceDailyRecords, iniList, termAttendanceRules);
        attendanceDailyRecords.forEach(attendanceDailyRecord -> {
            //封装通行记录到排班对象中
            groupByIdCard.forEach((idCardKey, listOfValue) -> {
                if (attendanceDailyRecord.getIdCard().equals(idCardKey)) {
                    attendanceDailyRecord.setAttendanceRecords(listOfValue);
                }
            });
            //--------------fixed 2021-12-02 考勤规则封装-----------------------
            attendanceDailyRecord.setTermAttendanceRules(termAttendanceRules);
            attendanceDailyRecord.setWorkTime(workTime);
            // 找到第一个上班时间和最后一个下班时间
            attendanceDailyRecord.setFirstInTime(attendanceDailyRecord.getFirstBeginTime());
            attendanceDailyRecord.setLastOutTime(attendanceDailyRecord.getThreeEndTime());
            //触发考勤统计对象计算考勤
            attendanceDailyRecord.calculateAttendanceResult(workTime);
        });
        //5.每日统计汇总 班组分组{"deptId":[{"shiftId":[{},{},{}]},{},{}]}
        List<AttendanceSumDailyRecord> attendanceSumDailyRecords = getAttendanceSumDailyRecords(workTime, attendanceDailyRecords);
        //6.个人考勤信息and每日考勤信息汇总写入数据库
        Map<String, String> map = new HashMap<>();
        attendanceSumDailyRecords.forEach(attendanceDailyInformation -> {
            map.put(String.valueOf(attendanceDailyInformation.getDeptId()), attendanceDailyInformation.getId());
        });
        attendanceDailyRecords.forEach(attendanceDailyRecord -> {
            attendanceDailyRecord.setSumRecordId(map.get(attendanceDailyRecord.getDeptId()));
        });
        /*attendanceDailyRecords.forEach(attendanceDailyRecord -> {
            attendanceSumDailyRecords.forEach(attendanceDailyInformation -> {
                System.out.println(attendanceDailyRecord.getDeptId());
                System.out.println(String.valueOf(attendanceDailyInformation.getDeptId()));
                if (StringUtils.equals(attendanceDailyRecord.getDeptId(), String.valueOf(attendanceDailyInformation.getDeptId()))) {
                    attendanceDailyRecord.setSumRecordId(attendanceDailyInformation.getId());
                }
            });
        });*/
        // 保存当日总的考勤记录汇总
        workAttendanceMapper.saveAttendanceSumDailyInformations(attendanceSumDailyRecords);
        workAttendanceMapper.saveAttendanceDailyInformations(attendanceDailyRecords);
    }

    //计算考勤汇总
    private List<AttendanceSumDailyRecord> getAttendanceSumDailyRecords(String workTime, List<AttendanceDailyRecord> mergeResult) {
        List<AttendanceSumDailyRecord> sumDailyRecords = Lists.newArrayList();
        mergeResult.parallelStream().collect(Collectors.groupingBy(AttendanceDailyRecord::getDeptId)).forEach((key, attendanceInformationList) -> {
            AttendanceSumDailyRecord attendanceSumDailyInformation = new AttendanceSumDailyRecord(Integer.parseInt(key), workTime, attendanceInformationList);
            AttendanceDailyRecord attendanceDailyRecord1 = attendanceInformationList.get(0);
            attendanceSumDailyInformation.setDeptName(attendanceDailyRecord1.getDeptName());
            attendanceSumDailyInformation.sumAttendance();
            sumDailyRecords.add(attendanceSumDailyInformation);
        });
        return sumDailyRecords;
    }

    /**
     * 计算次日日期
     **/
    private String calculateDate(String baseDate, int amount) {
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(DateUtils.dateTime(Constant.DATE_FORMAT_2, baseDate));
        calendar.add(Calendar.DATE, amount);
        return DateUtils.dateTime(calendar.getTime());
    }

    //考勤结果统计
    private void calculateAttendanceResult(Map<String, Object> requestParam, List<AttendanceDailyRecord> attendanceDailyRecords, List<MonthlyAttendanceIni> iniList, TermAttendanceRules termAttendanceRules) throws Exception {
        //考勤结果分组统计 上班考勤时间/下班考勤时间
        //考勤-打卡上班数据集合
        List<MonthlyAttendanceIni> inToWorkList = Lists.newArrayList();
        //考勤- 打卡下班数据集合
        List<MonthlyAttendanceIni> outToWorkList = Lists.newArrayList();
        //以出入口进行分组数据清洗
        Map<String, List<MonthlyAttendanceIni>> addressGroup = iniList.stream().parallel().collect(Collectors.groupingBy(MonthlyAttendanceIni::getAddress));
        addressGroup.forEach((key, attendanceInformationList) -> {
            //分组合并
            if (key.equals(Constant.NUMBER_88) || key.equals(Constant.NUMBER_92)) {
                //入口数据-> 上班时间
                inToWorkList.addAll(attendanceInformationList);
            } else if (key.equals(Constant.NUMBER_89) || key.equals(Constant.NUMBER_91)) {
                //出口数据 -> 下班时间
                outToWorkList.addAll(attendanceInformationList);
            }
        });
        //出入考勤记录分组 身份证:[{},{},{}]
        this.dataGroup(requestParam, inToWorkList, 1, attendanceDailyRecords, termAttendanceRules);
        this.dataGroup(requestParam, outToWorkList, 2, attendanceDailyRecords, termAttendanceRules);
    }

    //数据分组
    private void dataGroup(Map<String, Object> requestParam, List<MonthlyAttendanceIni> data, int dataType, List<AttendanceDailyRecord> attendanceDailyRecords, TermAttendanceRules termAttendanceRules) {
        Map<String, List<MonthlyAttendanceIni>> groupInfos = data.parallelStream().collect(Collectors.groupingBy(MonthlyAttendanceIni::getIdCard));
        attendanceDailyRecords.forEach(attendanceDailyRecord -> {
            groupInfos.forEach((key, idCardInfo) -> {
                if (key.equals(attendanceDailyRecord.getIdCard())) {
                    attendanceDailyRecord.setTermAttendanceRules(termAttendanceRules);
                    if (1 == dataType) {
                        attendanceDailyRecord.setMonthlyAttendanceIniSignIn(idCardInfo);
                    } else if (2 == dataType) {
                        attendanceDailyRecord.setMonthlyAttendanceIniSignOut(idCardInfo);
                    }
                }
            });
        });
    }

    //文件信息导入
    private <T> List<T> baseImport(InputStream inputStream, Integer titleRows, Integer headerRows,
                                   boolean needVerify, Class<T> pojoClass) throws Exception {
        if (null == inputStream) {
            return null;
        } else {
            final ImportParams params = new ImportParams();
            params.setTitleRows(titleRows);
            params.setHeadRows(headerRows);
            params.setNeedVerify(true);
            params.setNeedSave(false);
            return ExcelImportUtil.importExcel(inputStream, pojoClass, params);
        }
    }

    /**
     * @param file
     * @return void
     * @Description : 通行记录保存(导入)
     * @Author : leizhang
     * @Date 10:03 上午 2021/11/21
     **/
    @Override
    public AjaxResult saveAttendancePassRecord(MultipartFile file) throws Exception {
        List<MonthlyAttendanceIni> iniList = Lists.newArrayList();
        try {
            //导入日考勤数据
            iniList = baseImport(file.getInputStream(), 0, 1, false, MonthlyAttendanceIni.class);
        } catch (Exception e) {
            LOGGER.error("员工通行记录导入失败:[{}]", e.getMessage(), e);
        }
        if (iniList.isEmpty()) {
            throw new Exception("考勤数据结果错误");
        }
        /**空的身份证**/
        List<MonthlyAttendanceIni> emptyIdCard = iniList.stream().filter(monthlyAttendanceIni ->
                StringUtils.isEmpty(monthlyAttendanceIni.getIdCard())
        ).collect(Collectors.toList());
        if (!iniList.isEmpty() && emptyIdCard.isEmpty()) {
            workAttendanceMapper.saveAttendancePassRecord(iniList);
            return AjaxResult.success("导入成功");
        } else {
            String userNameStr = Joiner.on(",").join(emptyIdCard.stream().map(monthlyAttendanceIni -> monthlyAttendanceIni.getUserName()).collect(Collectors.toSet()));
            return AjaxResult.error(userNameStr.concat(" 无身份证信息"));
        }
    }

    /**
     * @param attendanceSumDailyRecord
     * @return java.util.List<com.ruoyi.worker.domain.workAttendance.AttendanceSumDailyRecord>
     * @Description : 考勤信息汇总
     * @Author : leizhang
     * @Date 3:24 下午 2021/11/21
     **/
    @Override
    public List<AttendanceSumDailyRecord> listOfAttendanceSumRecords(AttendanceSumDailyRecord attendanceSumDailyRecord) {
        return workAttendanceMapper.listOfAttendanceSumRecords(attendanceSumDailyRecord);
    }


    /**
     * @param attendanceDailyRecord
     * @return java.util.List<com.ruoyi.worker.domain.workAttendance.AttendanceDailyRecord>
     * @Description : 个人考勤记录查询
     * @Author : leizhang
     * @Date 4:31 下午 2021/11/21
     **/
    @Override
    public List<AttendanceDailyRecord> listOfAttendanceRecord(AttendanceDailyRecord attendanceDailyRecord) {
        return workAttendanceMapper.listOfAttendanceRecords(attendanceDailyRecord);
    }


    /**
     * @param monthlyAttendanceIni
     * @return java.util.List<com.ruoyi.worker.domain.MonthlyAttendanceIni>
     * @Description : 个人通行记录查询
     * @Author : leizhang
     * @Date 6:52 下午 2021/11/21
     **/
    @Override
    public List<MonthlyAttendanceIni> listOfMonthlyAttendanceIni(Map<String, Object> monthlyAttendanceIni) {
        return workAttendanceMapper.listOfAttendancePassRecord(monthlyAttendanceIni);
    }

    /**
     * @param file
     * @return void
     * @Description : 考勤数据导入修改
     * 1.导入的考勤数据对应的班组/班次必须为用一班组，同一班次
     * 2.增删改后的考勤数据导入怎么计算：
     * 1.以时间/班组/班次 取原始数据
     * 2.导入数据与原始数据对比，1）如果导入数据与原始数据重叠，取导入的数据 否则去原始数据 2）导入数据与原始数据重组计算汇总数据
     * @Author : leizhang
     * @Date 1:32 下午 2021/11/23
     **/
    @Override
    public void updateAttendanceInfo(MultipartFile file) {
        //1.导入修改考勤数据
        List<AttendanceDailyRecord> attendanceDailyRecords = Lists.newArrayList();
        try {
            //导入日考勤数据
            attendanceDailyRecords = baseImport(file.getInputStream(), 0, 1, false, AttendanceDailyRecord.class);
        } catch (Exception e) {
            LOGGER.error("员工通行记录导入失败:[{}]", e.getMessage(), e);
        }
        if (attendanceDailyRecords.isEmpty()) {
            throw new CustomException("导入考勤数据为空");
        }
        //2.导入数据解析处理
        int attendanceDailyRecordsSize = attendanceDailyRecords.size();
        final String workTime = attendanceDailyRecords.get(Constant.NUMBER_0).getWorkTime();
        if (attendanceDailyRecordsSize > 1) {
            for (int i = 0, j = i + 1; j <= attendanceDailyRecordsSize; i++) {
                //判断当前的工作时间，班组，班次与下一个是否一直
                AttendanceDailyRecord attendanceDailyRecordFirst = attendanceDailyRecords.get(i);
                AttendanceDailyRecord attendanceDailyRecordSecond = attendanceDailyRecords.get(j);
                if (attendanceDailyRecordFirst.getWorkTime().equals(attendanceDailyRecordSecond.getWorkTime()) ||
                        attendanceDailyRecordFirst.getShiftName().equals(attendanceDailyRecordSecond.getShiftName()) ||
                        attendanceDailyRecordFirst.getDeptName().equals(attendanceDailyRecordSecond.getDeptName())
                ) {
                    throw new CustomException("当前导入数据班组/班次/考勤时间不一致");
                }
            }
        }
        //3.获取原始数据
        ArrayList<AttendanceDailyRecord> mergeResult = Lists.newArrayList();
        List<AttendanceDailyRecord> attendanceDailyRecordsOriginal = workAttendanceMapper.listOfAttendanceRecordByWorkTime(attendanceDailyRecords.get(Constant.NUMBER_0));
        //以身份证id_card过滤,导入的数据与原始数据不重合的部分
        attendanceDailyRecords.stream().forEach(attendanceDailyRecord -> {
            attendanceDailyRecordsOriginal.stream().forEach(original -> {
                if (!attendanceDailyRecord.getIdCard().equals(original.getIdCard())) {
                    mergeResult.add(original);
                }
            });
        });
        //4.导入数据 跟原始数据合并 （导入的idCard 与 原始的idCard 不一致）
        mergeResult.addAll(attendanceDailyRecords);
        //5.重新计算考勤汇总数据
        List<AttendanceSumDailyRecord> sumDailyRecords = getAttendanceSumDailyRecords(workTime, mergeResult);
        //6.写入数据库
        workAttendanceMapper.saveAttendanceSumDailyInformations(sumDailyRecords);
        workAttendanceMapper.saveAttendanceDailyInformations(mergeResult);
    }

    /**
     * @param ids
     * @return java.util.List<com.ruoyi.worker.domain.workAttendance.AttendanceDailyRecord>
     * @Description : 考勤信息查询 byIds
     * @Author : leizhang
     * @Date 5:21 下午 2021/11/23
     **/
    @Override
    public List<AttendanceDailyRecord> listOfAttendanceRecordByIds(int[] ids) {
        return workAttendanceMapper.listOfAttendanceRecordsBuIds(ids);
    }

    /**
     * @param attendanceMonthSumRecord
     * @return java.util.List<com.ruoyi.worker.domain.workAttendance.AttendanceMonthSumRecord>
     * @Description : 月考勤汇总
     * @Author : leizhang
     * @Date 3:25 下午 2021/11/24
     **/
    @Override
    public List<AttendanceMonthSumRecord> listOfAttendanceSumReCord(AttendanceMonthSumRecord attendanceMonthSumRecord) {
        return workAttendanceMapper.listOfAttendanceMonthSumRecords(attendanceMonthSumRecord);
    }


    /**
     * @param attendanceMonthRecord
     * @return java.util.List<com.ruoyi.worker.domain.workAttendance.AttendanceMonthRecord>
     * @Description : 月考勤数据
     * @Author : leizhang
     * @Date 3:28 下午 2021/11/24
     **/
    @Override
    public List<AttendanceMonthRecord> listOfAttendanceMonthRecord(AttendanceMonthRecord attendanceMonthRecord) throws ParseException {
        List<AttendanceMonthRecord> attendanceMonthRecords = workAttendanceMapper.listOfAttendanceMonthRecords(attendanceMonthRecord);
        RzScheduleStaffPlan plan = new RzScheduleStaffPlan();
        plan.setDeptId((long) attendanceMonthRecord.getDeptId());
        plan.setScheduleDate(new SimpleDateFormat(Constant.DATE_FORMAT_2).parse(attendanceMonthRecord.getWorkTime()));
        List<RzScheduleStaffPlan> rzScheduleStaffPlans = rzScheduleStaffPlanMapper.selectRzScheduleStaffPlanListAttendance(plan);
        Map<String, List<RzScheduleStaffPlan>> groupByIdCard = rzScheduleStaffPlans.stream().collect(Collectors.groupingBy(RzScheduleStaffPlan::getIdCard));
        attendanceMonthRecords.forEach(attendanceMonthRecord1 -> {
            groupByIdCard.forEach((key, value) -> {
                if (attendanceMonthRecord1.getIdCard().equals(key)) {
                    attendanceMonthRecord1.setStaffPlans(value);
                }
            });
            attendanceMonthRecord1.calculateAttendanceDat();
        });
        return attendanceMonthRecords;
    }

    /**
     * @param attendanceDailyRecord
     * @return java.util.List<com.ruoyi.worker.domain.workAttendance.vo.AttendanceMonthRecordVo>
     * @Description : 月考勤记录导出
     * @Author : leizhang
     * @Date 1:51 下午 2021/11/25
     **/
    @Override
    public Map<String, Object> listOfAttendanceRecordVo(AttendanceDailyRecord attendanceDailyRecord) {
        List<AttendanceMonthRecordVo> result = Lists.newArrayList();
        //excel 导出表头
        Set<String> headTitle = Sets.newHashSet();
        List<AttendanceDailyRecord> attendanceDailyRecords = workAttendanceMapper.listOfAttendanceRecordExport(attendanceDailyRecord);
        Map<String, List<AttendanceDailyRecord>> groupByIdCard = attendanceDailyRecords.parallelStream().collect(Collectors.groupingBy(AttendanceDailyRecord::getIdCard));
        groupByIdCard.forEach((key, value) -> {
            AttendanceMonthRecordVo attendanceMonthRecordVo = new AttendanceMonthRecordVo(value.get(Constant.NUMBER_0));
            List<AttendanceMonthRecordDto> dtoList = Lists.newArrayList();
            value.stream().forEach(attendanceDailyRecord1 -> {
                String worTime = attendanceDailyRecord1.getWorkTime();
                headTitle.add(worTime);
                dtoList.add(new AttendanceMonthRecordDto(worTime, attendanceDailyRecord1.getAbnormalAttendance() == 0 ? attendanceDailyRecord1.getShiftName() : String.valueOf(attendanceDailyRecord1.getAbnormalAttendance())));
            });
            attendanceMonthRecordVo.setAttendanceMonthRecordDtoList(dtoList);
            result.add(attendanceMonthRecordVo);
        });
        return ImmutableMap.of("data", result, "headTitle", headTitle);
    }

    /**
     * @param attendanceDailyRecord
     * @return java.util.List<com.ruoyi.worker.domain.workAttendance.AttendanceDailyRecord>
     * @Description : 个人考勤信息(月考勤)
     * @Author : leizhang
     * @Date 5:13 下午 2021/11/25
     **/
    @Override
    public List<AttendanceDailyRecord> listOfAttendanceRecordByIdCard(AttendanceDailyRecord attendanceDailyRecord) {
        return workAttendanceMapper.listOfAttendanceRecordExport(attendanceDailyRecord);
    }
}
